#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<unordered_map>
#include<cstring>
#include<sys/stat.h>
#include<fcntl.h>

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

// shell定义的全局数据

// 1.命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0;

// 2.环境变量表
#define MAX_ENVS 100
char* g_env[MAX_ENVS];
int g_envs = 0;

// 3.别名映射表
std::unordered_map<std::string, std::string> alias_list;

// test
char cwd[1024];
char cwdenv[1024];

// 4.退出状态 last exit code
int lastcode = 0;

// 5.关于重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

int redir =  NONE_REDIR;
std::string filename;

const char *GetUserName()
{
    const char *name = getenv("USER");
    return name == NULL ? "None" : name;
}

const char *GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

const char *GetPwd()
{
    // const char *pwd = getenv("PWD");
   const char *pwd = getcwd(cwd, sizeof(cwd));
   if(pwd != NULL)
   {
       snprintf(cwdenv,sizeof(cwdenv), "PWD=%s",cwd);
       putenv(cwdenv);
   }
    return pwd == NULL ? "None" : pwd;
}

const char *GetHome()
{
    const char *home = getenv("HOME");
    return home == NULL ? "" : home;
}

void InitEnv()
{
    extern char** environ;
    memset(g_env, 0, sizeof(g_env));
    g_envs = 0;

    // 本来要从配置文件来，此处从父进程获取
    // 1.获取环境变量
    for(int i = 0; environ[i]; i++)
    {
        // 1.申请空间
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i], environ[i]);
        g_envs++;
    }
    g_env[g_envs++] = (char*)"HAHA=for_test";    // for test 
    g_env[g_envs] = NULL;

    // 2.导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

std::string DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if(dir == SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos) return "BUG?";
    return dir.substr(pos+1);
}

void MakeCommandLine(char cmd_prompt[], int size)
{
    //snprintf(cmd_prompt,size,FORMAT, GetUserName(), GetHostName(), GetPwd());
    snprintf(cmd_prompt,size,FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}
// 1.打印命令行提示符
void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt, COMMAND_SIZE);
    printf("%s",prompt);
    fflush(stdout);
}
// 2.获取命令
bool GetCommandLine(char* out, int size)
{
    char *c = fgets(out, size, stdin);// fgets读取到'\n'时结束
    if(c == NULL) return false;
    out[strlen(out) - 1] = 0;//清理\n
    if(strlen(out) == 0) return false;
    return true;
}

// 3.命令行分析(分割) "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
    g_argc = 0;
    // 命令行分析"ls -a -l" -> "ls" "-a" "-l"
    g_argv[g_argc++] = strtok(commandline, SEP);
    while((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));
    g_argc--;
    return g_argc > 0;
}

void PrintArgv()
{
    for(int i = 0; g_argv[i];i++)
    {
        printf("argv[%d]->%s\n",i,g_argv[i]);
    }
    printf("argc:%d\n",g_argc);
}

bool Cd()
{
    if(g_argc == 1)
    {
        std::string home = GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());    // 改变当前进程的目录
    }
    else
    {
        std::string where = g_argv[1];
        if(where == "-")
        {

        }
        else if(where == "~")
        {
            std::string home = GetHome();
            if(home.empty()) return true;
            chdir(home.c_str());    // 改变当前进程的目录
        }
        else 
        {
            chdir(where.c_str());
        }
    }
}

bool Echo()
{
    if(g_argc == 2)
    {
        // echo "helloe world"
        // echo $?
        // echo $PATH
        std::string opt = g_argv[1];
        if(opt == "$?")
        {
            std::cout << lastcode << std::endl;
            lastcode = 0;
        }
        else if(opt == "$")
        {
            std::string env_name = opt.substr(1);
            const char *env_value = getenv(env_name.c_str());
            if(env_value)
                std::cout << env_value << std::endl;
        }
        else
        {
            std::cout << opt << std::endl;
        }
    }
}

bool CheckAndExecBuiltin()
{
    std::string cmd = g_argv[0];
    if(cmd == "cd")
    {
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
        Echo();
        return true;
    }
    else if(cmd == "export")
    {
        // Export();
        return true;
    }
    else if(cmd == "alias")
    {
        // Alias();
        return true;
    }
    return false;
}

// 4.执行命令
int Execute()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        int fd = -1;
        if(redir == INPUT_REDIR)
        {
            fd = open(filename.c_str(), O_RDONLY);
            if(fd < 0)  exit(1);
            dup2(fd, 0);
            close(fd);
        }
        else if(redir == OUTPUT_REDIR)
        {
            fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
            if(fd < 0)  exit(1);
            dup2(fd, 1);
            close(fd);
        }
        else if(redir == APPEND_REDIR)
        {
            fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
            if(fd < 0)  exit(1);
            dup2(fd, 1);
            close(fd);
        }
        

        // 进程替换
        execvp(g_argv[0],g_argv);
        exit(1);
    }
    // father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        lastcode = WEXITSTATUS(status);
    }
    return 0;
}

void TrimSpace(char cmd[], int &end)
{
    while(isspace(cmd[end]))
        end++;
}

void RedirCheck(char cmd[])
{
    redir = NONE_REDIR;
    filename.clear();
    int start = 0;
    int end = strlen(cmd) - 1;

    //"ls -a -l >> file.txt" > >> <
    while(end > start)
    {
        if(cmd[end] == '<')
        {
            cmd[end++] = 0;
            TrimSpace(cmd, end);
            redir = INPUT_REDIR;
            filename = cmd + end;
            break;
        }
        else if(cmd[end] == '>')
        {
            if(cmd[end - 1] == '>')
            {
                cmd[end - 1] = 0;
                redir = APPEND_REDIR;
            }
            else
            {
                redir = OUTPUT_REDIR;
            }
            cmd[end++] = 0;
            TrimSpace(cmd, end);
            filename = cmd + end;
            break;
        }
        else
        {
            end--;
        }
    }

}

int main()
{
    // shell启动的时候，从系统中获取环境变量
    // 我们的环境变量信息应该从父shell统一来
    InitEnv();
    while(true)
    {
        
        // 1.打印命令行提示符
        PrintCommandPrompt();
        
        // 2.获取用户输入的命令
        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline, COMMAND_SIZE))
            continue;

        // 3.重定向分析"ls -a -l > file.txt" -> "la -a -l" "file.txt" -> 判定重定向方式
        RedirCheck(commandline);

        // 4.命令行分析(分割) "ls -a -l" -> "ls" "-a" "-l"
        CommandParse(commandline);
        //PrintArgv();
    
        // 5.检测并处理内建命令
        if(CheckAndExecBuiltin())
            continue;

        // 6.执行命令
        Execute();
    }
    return 0;
}
